import { startTimer } from 'winston';
import { Injectable } from '@nestjs/common';
import { CreateMatchDto } from './dto/create-match.dto';
import { UpdateMatchDto } from './dto/update-match.dto';
import { MatchInfo } from './entities/match.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { In, IsNull, Repository } from 'typeorm';
import { MatchStatusEnum } from 'src/common/constant/enums';
import { SearchMatchesDto } from './dto/search-match.dto';

@Injectable()
export class MatchService {
  constructor(
    @InjectRepository(MatchInfo)
    private readonly repository: Repository<MatchInfo>,
  ) {}
  async create(createMatchDto: CreateMatchDto) {
    const entity = this.repository.create(createMatchDto);
    return await this.repository.save(entity);
  }

  async save(matchData: CreateMatchDto | MatchInfo) {
    // 检查是否为已存在的实体（有id）
    const entity = this.repository.create(matchData);
    if (typeof matchData === 'object' && 'id' in matchData && matchData.id) {
      // 对于已存在的实体，使用 update 方法
      // return await this.repository.save(matchData);
      if (!entity.games?.length) {
        delete entity.games;
      }
      if (!entity.subMatchInfos?.length) {
        delete entity.subMatchInfos;
      }
      delete entity.homePlayer;
      delete entity.awayPlayer;
      return await this.repository.save(entity);
    } else {
      // 对于新实体，使用 create 方法
      return await this.create(entity);
    }
  }

  async findOneById(id: number) {
    return await this.repository.findOne({
      where: {
        id,
      },
    });
  }
  async listInIds(ids: number[]) {
    return await this.repository
      .createQueryBuilder('matchInfo')
      .where('matchInfo.id IN (:...ids)', { ids })
      .getMany();
  }
  async findById(id: number) {
    return await this.repository
      .createQueryBuilder('matchInfo')
      // .leftJoinAndSelect('matchInfo.parentMatchInfo', 'parentMatchInfo')
      // .select(['matchInfo', 'gameRule.minWinGames', 'gameRule.maxWinGames'])
      // .leftJoinAndSelect('matchInfo.gameRule', 'gameRule')
      .leftJoinAndSelect('matchInfo.subMatchInfos', 'subMatchInfos')
      .where('matchInfo.id = :id', { id })
      .getOne();
  }

  async listByIds(ids: number[]) {
    return await this.repository
      .createQueryBuilder('matchInfo')
      .leftJoinAndSelect('matchInfo.homePlayer', 'homePlayer')
      .leftJoinAndSelect('matchInfo.awayPlayer', 'awayPlayer')
      .where('matchInfo.id IN (:...ids)', { ids })
      .getMany();
  }

  async update(id: number, updateMatchDto: UpdateMatchDto) {
    const updateData: Partial<MatchInfo> = {};
    for (const [key, value] of Object.entries(updateMatchDto)) {
      if (
        typeof value !== 'object' ||
        value === null ||
        value instanceof Date
      ) {
        updateData[key] = value;
      }
    }
    const entity = this.repository.create(updateData);
    return await this.repository.update(id, entity);
  }

  async getMatchByGroupIdAndGroupMatchIndex(
    // 这个地方需要携带subMatchInfo
    groupId: number,
    groupMatchIndex: number,
  ) {
    // return await this.repository.findOne({
    //   where: {
    //     group: {
    //       id: groupId,
    //     },
    //     groupMatchIndex,
    //   },
    // });
    const matchInfos = await this.repository
      .createQueryBuilder('matchInfo')
      .leftJoinAndSelect('matchInfo.subMatchInfos', 'subMatchInfos')
      .where('matchInfo.groupId = :groupId', { groupId })
      .andWhere('matchInfo.groupMatchIndex = :groupMatchIndex', {
        groupMatchIndex,
      })
      .getOne();
    return matchInfos;
  }

  // async remove(id: number) {
  //   return await this.repository.delete(id);
  // }
  async saveList(updateMatchDtoList: Array<UpdateMatchDto>) {
    const entities = updateMatchDtoList.map((v) => {
      if (v.id) {
        v.id = parseInt(v.id as any);
      }
      delete v.homePlayer;
      delete v.awayPlayer;
      const ret = this.repository.create(v);
      return ret;
    });
    return await this.repository.save(entities);
  }

  async getTournamentMatchesSimple(
    tournamentId: number,
    playgroundId?: number,
  ) {
    const query = this.repository
      .createQueryBuilder('matchInfo')
      .where('matchInfo.tournamentId = :tournamentId', { tournamentId });
    if (playgroundId) {
      query.andWhere('matchInfo.playgroundId = :playgroundId', {
        playgroundId,
      });
    }
    return await query.getMany();
  }
  async getMatchesByTournamentId(tournamentId: number) {
    const matchInfos = await this.repository
      .createQueryBuilder('matchInfo')
      .where('matchInfo.tournamentId = :tournamentId', { tournamentId })
      .leftJoinAndSelect('matchInfo.homePlayer', 'homePlayer')
      .leftJoinAndSelect('matchInfo.awayPlayer', 'awayPlayer')
      .leftJoin('matchInfo.playground', 'playground')
      .addSelect(['playground.name', 'playground.id'])
      .leftJoin('matchInfo.tournament', 'tournament')
      .addSelect('tournament.name')
      .leftJoin('matchInfo.category', 'category')
      .addSelect('category.name')
      .leftJoin('matchInfo.event', 'event')
      .addSelect(['event.name'])
      .leftJoin('matchInfo.stage', 'stage')
      .addSelect(['stage.name', 'stage.stageOrderInEvent'])
      .leftJoin('matchInfo.group', 'group')
      .addSelect('group.name')

      .leftJoinAndSelect('matchInfo.subMatchInfos', 'subMatchInfos')
      .leftJoinAndSelect('subMatchInfos.homePlayer', 'subHomePlayer')
      .leftJoinAndSelect('subMatchInfos.awayPlayer', 'subAwayPlayer')
      .leftJoin('subMatchInfos.playground', 'subMatchPlayground')
      .addSelect(['subMatchPlayground.id', 'subMatchPlayground.name'])

      .getMany();
    matchInfos.forEach((matchItem) => {
      (matchItem as any).stageOrderInEvent = matchItem.stage.stageOrderInEvent;
      [matchItem.homePlayer, matchItem.awayPlayer, matchItem.winner].forEach(
        (v) => {
          if (!v) {
            return;
          }
          if (v.player1Name) {
            v.player = { name: v.player1Name };
          }
          if (v.player2Name) {
            v.player2 = { name: v.player2Name };
          }
          if (v.teamName) {
            (v as any).team = { name: v.teamName };
          }
        },
      );
    });
    return matchInfos;
  }
  async getMatchDetail(matchId: number) {
    const queryBuilder = this.repository
      .createQueryBuilder('matchInfo')
      .where('matchInfo.id = :matchId', { matchId })
      .leftJoinAndSelect('matchInfo.games', 'games')
      .leftJoinAndSelect('matchInfo.playground', 'playground')
      .addSelect('playground.name', 'playground.id')
      .leftJoinAndSelect('matchInfo.rallyRule', 'rallyRule')
      .leftJoinAndSelect('matchInfo.gameRule', 'gameRule')
      .leftJoinAndSelect('matchInfo.homePlayer', 'homePlayer')
      .leftJoinAndSelect('matchInfo.awayPlayer', 'awayPlayer')
      .leftJoinAndSelect('matchInfo.subMatchInfos', 'subMatchInfos')
      .leftJoinAndSelect('subMatchInfos.games', 'subGames')
      .leftJoinAndSelect('subMatchInfos.homePlayer', 'subHomePlayer')
      .leftJoinAndSelect('subMatchInfos.awayPlayer', 'subAwayPlayer')
      .leftJoinAndSelect('subMatchInfos.winner', 'subWinner')
      .leftJoinAndSelect('subMatchInfos.rallyRule', 'subRallyRule')
      .leftJoinAndSelect('subMatchInfos.gameRule', 'subGameRule')
      .leftJoinAndSelect('matchInfo.parentMatchInfo', 'parentMatchInfo')
      .leftJoinAndSelect('parentMatchInfo.homePlayer', 'parentHomePlayer')
      .leftJoinAndSelect('parentMatchInfo.awayPlayer', 'parentAwayPlayer')
      .leftJoinAndSelect('parentMatchInfo.group', 'parentGroup')
      .leftJoin('matchInfo.tournament', 'tournament')
      .addSelect('tournament.name')
      .leftJoin('matchInfo.category', 'category')
      .addSelect('category.name')
      .leftJoin('matchInfo.event', 'event')
      .addSelect(['event.name'])
      .leftJoin('matchInfo.stage', 'stage')
      .addSelect('stage.name')
      .leftJoin('matchInfo.group', 'group')
      .addSelect('group.name');

    // if (showGameProgress) {
    //   queryBuilder.addSelect('games.progress');
    //   queryBuilder.addSelect('subGames.progress');
    // }

    const matchInfos = await queryBuilder.getOne();

    [matchInfos.homePlayer, matchInfos.awayPlayer, matchInfos.winner].forEach(
      (v) => {
        if (!v) {
          return;
        }
        if (v.player1Name) {
          v.player = { name: v.player1Name };
        }
        if (v.player2Name) {
          v.player2 = { name: v.player2Name };
        }
        if (v.teamName) {
          (v as any).team = { name: v.teamName };
        }
      },
    );
    return matchInfos;
  }
  // async getMatchesByStatusAndTournamentId(
  //   tournamentId: number,
  //   status: MatchStatusEnum,
  // ) {
  //   const matchInfos = await this.repository
  //     .createQueryBuilder('matchInfo')
  //     .leftJoinAndSelect('matchInfo.games', 'games')
  //     .leftJoinAndSelect('matchInfo.group', 'group')
  //     .leftJoinAndSelect('matchInfo.playground', 'playground')
  //     .leftJoinAndSelect('group.stage', 'stage')
  //     .leftJoinAndSelect('stage.event', 'event')
  //     .leftJoinAndSelect('event.category', 'category')
  //     .leftJoinAndSelect('category.tournament', 'tournament')
  //     .where('tournament.id = :tournamentId', { tournamentId })
  //     .andWhere('matchInfo.status = :status', { status })
  //     .getMany();
  //   return matchInfos;
  // }
  async removeInStageIds(stageIds: Array<number>) {
    const deleteData = await this.repository
      .createQueryBuilder('matchInfo')
      .leftJoinAndSelect('matchInfo.group', 'group')
      .leftJoinAndSelect('group.stage', 'stage')
      .where('stage.id IN (:...stageIds)', { stageIds })
      .getMany();
    const parentIds = deleteData.map((v) => {
      return v.id;
    });
    const subMatchInfos = await this.repository
      .createQueryBuilder('matchInfo')
      .where('matchInfo.fk_subMatchId IN (:...parentIds)', { parentIds })
      .getMany();
    // 这里考虑团队赛，有父比赛，有子比赛都要删除
    await this.repository.remove([...subMatchInfos]);
    await this.repository.remove([...deleteData]);
  }

  async getMatchesByConditions(conditions: SearchMatchesDto) {
    const queryBuilder = this.repository
      .createQueryBuilder('matchInfo')
      .leftJoinAndSelect('matchInfo.subMatchInfos', 'subMatchInfos')
      .leftJoinAndSelect('matchInfo.playground', 'playground')
      .leftJoinAndSelect('matchInfo.group', 'group')
      .leftJoinAndSelect('group.stage', 'stage')
      .leftJoinAndSelect('stage.event', 'event');
    // .leftJoinAndSelect('event.eventToPlayers', 'eventToPlayers');
    // .leftJoinAndSelect('eventToPlayers.player', 'player')
    // .leftJoinAndSelect('eventToPlayers.player2', 'player2');

    if (conditions.eventId && conditions.eventId.length > 0) {
      queryBuilder.andWhere('event.id IN (:...eventIds)', {
        eventIds: conditions.eventId,
      });
    }

    if (conditions.stageType && conditions.stageType.length > 0) {
      queryBuilder.andWhere('stage.type IN (:...stageTypes)', {
        stageTypes: conditions.stageType,
      });
    }

    if (conditions.scheduleType && conditions.scheduleType.length > 0) {
      const scheduledCondition =
        'matchInfo.playgroundId IS NOT NULL AND matchInfo.estimatedStart IS NOT NULL AND matchInfo.estimatedEnd IS NOT NULL';
      const unscheduledCondition =
        'matchInfo.playgroundId IS NULL OR matchInfo.estimatedStart IS NULL OR matchInfo.estimatedEnd IS NULL';

      const scheduleConditions = conditions.scheduleType.map((type) =>
        type === 1 ? `(${scheduledCondition})` : `(${unscheduledCondition})`,
      );

      queryBuilder.andWhere(`(${scheduleConditions.join(' OR ')})`);
    }

    if (conditions.statusType && conditions.statusType.length > 0) {
      queryBuilder.andWhere('matchInfo.status IN (:...statusTypes)', {
        statusTypes: conditions.statusType,
      });
    }

    if (
      conditions.groupLoopIndexType &&
      conditions.groupLoopIndexType.length > 0
    ) {
      queryBuilder.andWhere(
        'matchInfo.groupLoopIndex IN (:...groupLoopIndexTypes)',
        { groupLoopIndexTypes: conditions.groupLoopIndexType },
      );
    }

    const res = await queryBuilder.getMany();
    return res;
  }

  async handleGetMatchesByConditions(conditions: SearchMatchesDto) {
    const res = await this.getMatchesByConditions(conditions);
    res.forEach((matchItem: MatchInfo) => {
      matchItem.hasByePlayer =
        matchItem.homePlayer?.isByePlayer ||
        matchItem.awayPlayer?.isByePlayer ||
        false;
      [matchItem.homePlayer, matchItem.awayPlayer, matchItem.winner].forEach(
        (v) => {
          if (!v) {
            return;
          }
          if (v.player1Name) {
            v.player = { name: v.player1Name };
          }
          if (v.player2Name) {
            v.player2 = { name: v.player2Name };
          }
          if (v.teamName) {
            (v as any).team = { name: v.teamName };
          }
        },
      );
    });
    return res;
  }
  async getInProgressMatches(tournamentId: number) {
    // 获取正在进行中的比赛
    const matchInfos = await this.repository
      .createQueryBuilder('matchInfo')
      .leftJoinAndSelect('matchInfo.homePlayer', 'homePlayer')
      .leftJoinAndSelect('matchInfo.awayPlayer', 'awayPlayer')
      .leftJoinAndSelect('matchInfo.winner', 'winner')
      .leftJoin('matchInfo.group', 'group')
      .addSelect(['group.name', 'group.id'])
      // .leftJoinAndSelect('group.stage', 'stage')
      // .leftJoinAndSelect('stage.event', 'event')
      // .leftJoinAndSelect('event.category', 'category')
      // .leftJoinAndSelect('category.tournament', 'tournament')
      .leftJoin('matchInfo.playground', 'playground')
      .addSelect(['playground.name', 'playground.id'])
      .leftJoinAndSelect('matchInfo.games', 'games')
      .leftJoinAndSelect('matchInfo.parentMatchInfo', 'parentMatchInfo')
      .leftJoinAndSelect('parentMatchInfo.homePlayer', 'parentHomePlayer')
      .leftJoinAndSelect('parentMatchInfo.awayPlayer', 'parentAwayPlayer')
      // .leftJoinAndSelect('event.eventToPlayers', 'eventToPlayers')
      // .leftJoinAndSelect('eventToPlayers.player', 'player')
      // .leftJoinAndSelect('eventToPlayers.player2', 'player2')
      .where('matchInfo.tournamentId = :tournamentId', { tournamentId })
      .andWhere('matchInfo.status = :status', {
        status: MatchStatusEnum.InProgress,
      })
      .andWhere('matchInfo.playgroundId IS NOT NULL')
      .getMany();

    matchInfos.forEach((matchItem) => {
      [matchItem.homePlayer, matchItem.awayPlayer, matchItem.winner].forEach(
        (v) => {
          if (!v) {
            return;
          }
          if (v.player1Name) {
            v.player = { name: v.player1Name };
          }
          if (v.player2Name) {
            v.player2 = { name: v.player2Name };
          }
          if (v.teamName) {
            (v as any).team = { name: v.teamName };
          }
        },
      );
    });
    return matchInfos;
  }
  async updateParentMatch(matchId, newMatch: Partial<MatchInfo>) {
    const matchInfo = await this.findById(matchId);
    if (matchInfo.subMatchId) {
      await this.update(matchInfo.subMatchId, newMatch);
    }
  }

  async listByE2PIds(e2pIds: number[]) {
    const matches = await this.repository.find({
      where: [{ homePlayerId: In(e2pIds) }, { awayPlayerId: In(e2pIds) }],
    });
    return matches;
  }

  async removeIds(ids: number[]) {
    // this.repository.delete(ids);
    await this.repository.delete(ids);
  }

  async getByStageId(stageId: number) {
    return await this.repository.find({
      where: {
        stageId,
      },
    });
  }
  async getByGroupId(groupId: number) {
    return await this.repository.find({
      where: {
        groupId,
      },
      relations: ['subMatchInfos'],
    });
  }
  // async getMatchWithGamesByStageId(stageId: number) {
  //   return await this.repository.find({
  //     where: {
  //       stageId: stageId,
  //     },
  //     relations: ['games'],
  //   });
  // }
}
